const path = require("path");
const fs = require("fs-extra");
const { assign } = require("xe-utils");
const chalk = require("chalk");
const { camelCase } = require("change-case");
const { CONFIG_PARAM_COMMENT_STATEMENT } = require("./config");

/**
 * 生成模块映射, 用于生成api文件, 生成的文件在 src/api/module-map.json 文件中
 * 映射关系为: 模块名: 模块文件名
 * @param tags {array} swagger中的tags
 */
function generateModuleMap(tags) {
  const moduleMap = tags.reduce((acc, cur) => {
    acc[cur.name] = "";
    return acc;
  }, {});
  const moduleMapJson = path.join(__dirname, "../../src/api/module-map.json");
  const module = fs.readJSONSync(moduleMapJson, { throws: false });
  const result = assign({}, moduleMap, module);
  fs.writeJsonSync(moduleMapJson, result, { spaces: 2 });
  for (const key in result) {
    if (!result[key]) {
      console.log(
        chalk.yellowBright(
          `后端增加了新的模块: [${key}], 请检查 src/api/module-map.json 文件, 对新增的模块命名, 如果新增的模块和自己无关, 可忽略此提示`,
        ),
      );
    }
  }
  return result;
}

function convertType(type, format) {
  switch (type) {
    case "integer":
    case "long":
    case "number":
      return "number";
    case "string":
      if (format === "date-time" || format === "date") {
        return "string|YYYY-MM-DD HH:mm:ss";
      } else if (format === "binary") {
        return "string|binary";
      } else {
        return "string";
      }
    case "boolean":
      return "boolean";
    case "array":
      return "array";
    case "object":
      return "object";
    default:
      console.log({ type, format });
      throw new Error("未知类型");
  }
}

function getMethod(apiConfig) {
  const { get, post } = apiConfig;
  if (get) {
    return ["get", get];
  } else if (post) {
    return ["post", post];
  } else {
    // TODO console.log(chalk.redBright("不支持的请求方法, 待实现"));
  }
}

function getParamName(method, required, name, _in) {
  let parameterName;
  if (method === "get") {
    parameterName = "params.";
  } else {
    parameterName = _in === "query" ? "params." : "data.";
  }
  return required ? parameterName + name : `[${parameterName}${name}]`;
}

function getPramsComments(method, params) {
  if (params.length === 0) {
    return CONFIG_PARAM_COMMENT_STATEMENT;
  }
  const paramsComments = params.reduce((acc, cur) => {
    let paramType;
    let desc = "";
    const _in = cur._in;
    const { name, description, required, type, format, items, _enum, originalRef } = cur;
    if (type === "array") {
      if (items?.originalRef) {
        paramType = `array<${items.originalRef}>`;
      } else {
        if (items?.type) {
          paramType = `array<${convertType(items.type)}>`;
        } else {
          console.log({ cur });
          throw new Error("未处理的情况");
        }
      }
    } else if (originalRef) {
      paramType = originalRef;
    } else {
      const res = convertType(type, format).split("|");
      paramType = res[0];
      desc = res[1];
    }
    return `${acc}\n * @param {${paramType}} ${getParamName(method, required, name, _in)} ${description || name}${
      desc ? ` ${desc}` : ""
    }${_enum ? ` 可选值: ${_enum.join(", ")}` : ""}`;
  }, "");
  return `${paramsComments}${CONFIG_PARAM_COMMENT_STATEMENT}`;
}

function getMethodStr(moduleName, apiSummary, method, url, paramsComments, author, generic, params, contentType) {
  const _methodName = getMethodName(method, params, contentType);
  let _parameterName = "";
  let _param = "";
  let _parameter = "";
  const _author = author ? `\n * @author ${author}` : "";
  const _returnsComment = getReturnsComment(generic) || "\n * @returns {Promise}";
  if (paramsComments !== CONFIG_PARAM_COMMENT_STATEMENT) {
    _parameterName = getParameterName(method, _methodName);
    _param = getParam(_parameterName, paramsComments);
    _parameter = `, ${_parameterName}`;
  } else {
    _param = paramsComments;
  }
  const _returnStatement = getReturnStatement(url, method, params, _methodName, _parameter);
  return `
/**
 * ${moduleName} / ${apiSummary}${_param}${_returnsComment}${_author}
 */
export function ${camelCase(url.replace(/\//g, " "))}(${_parameterName}${_parameterName ? ", " : ""}config) {
  ${_returnStatement}
}
`;
}

function getParameterName(method, methodName) {
  if (method === "get") {
    return "params";
  } else {
    if (methodName === "postWithQueryParams") {
      return "params, data";
    } else {
      return "data";
    }
  }
}

function getParam(parameterName, paramsComments) {
  if (parameterName.includes(",")) {
    let paramStr = "";
    parameterName.split(", ").forEach((item) => {
      paramStr += `\n * @param {object} ${item}`;
    });
    return paramStr + paramsComments;
  } else {
    return `\n * @param {object} ${parameterName}${paramsComments}`;
  }
}

function getReturnsComment(generic) {
  if (generic?.length > 0) {
    const firstElement = generic[0];
    if (firstElement === "int") {
      return "\n * @returns {Promise<number>}";
    } else if (firstElement === "Void") {
      return "";
    }
    return `\n * @returns {Promise<${convertToGenericType(generic)}}`;
  }
}

function convertToGenericType(arr) {
  if (arr.length === 0) {
    return "";
  }

  const firstElement = arr[0];
  let result = "";
  result += `${firstElement}<`;
  result += convertToGenericType(arr.slice(1));

  if (result.charAt(result.length - 1) === "<") {
    // 移除多余的 '<'
    result = result.slice(0, result.length - 1);
  }
  result += ">";

  return result;
}

function getReturnStatement(url, method, params, methodName, parameter) {
  if (method === "get" && params?.[0]?._in === "path") {
    return `get({ url: "${url.replace(/\/{[\d\D]*/, "")}", pathParams: params, config });`;
  } else {
    return `return ${methodName}("${url}"${parameter}, config);`;
  }
}

function getMethodName(method, params, contentType) {
  if (contentType === "multipart/form-data") {
    return "postFormData";
  } else if (method === "post") {
    if (params.length >= 1 && params[0]._in === "query") {
      return "postWithQueryParams";
    } else {
      return "post";
    }
  } else {
    return method;
  }
}

module.exports = {
  generateModuleMap,
  convertType,
  getMethod,
  getPramsComments,
  getMethodStr,
};
